﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Markup;
using Rhizome.Contract;
using Rhizome.Contract.Diag;
using Rhizome.Internal;

namespace Rhizome.Model
{
	[ContentProperty("Items")]
	public abstract class RulesStateBase<TPlant, TItem> : StateBase<TPlant>, IForwardEvent, IStateInitialize, IStateActions,
	                                                      IMessageSink
	{
		protected RulesStateBase()
		{
			Items = new RhizomeElementCollection<TItem>(this);
		}

		public Collection<TItem> Items { get; private set; }

		#region IForwardEvent Members

		object IForwardEvent.GetForwardEvent()
		{
			return Items.GetForwardEvent();
		}

		#endregion

		#region IStateActions Members

		public void EntryAction()
		{
			ForEach<IStateActions>(item => item.EntryAction());
		}

		public void ExitAction()
		{
			ForEach<IStateActions>(item => item.ExitAction());
		}

		#endregion

		#region IStateInitialize Members

		public void Initialize()
		{
			ForEach<IStateInitialize>(item => item.Initialize());
		}

		public void Cleanup()
		{
			ForEach<IStateInitialize>(item => item.Cleanup());
		}

		#endregion

		protected void ForEach(Action<TItem> action)
		{
			foreach (var item in Items)
				action(item);
		}


		protected void ForEach<T>(Action<T> action)
		{
			foreach (var item in Items.OfType<T>())
				action(item);
		}

		protected override IEnumerable<IEnumerable> GetElementCollections()
		{
			return base.GetElementCollections().Concat(new[] {Items});
		}


		public override T GetValue<T>(RhizomeProperty<T> property)
		{
			if (ReferenceEquals(MessageSyncProperty, property))
				return (T) ((object) this);

			return base.GetValue(property);
		}

		#region Implementation of IMessageSink

		bool IMessageSink.Send(object source, IMessage message, ICollection<IMessageSink> stack)
		{
			if (Logger != null)
				Logger.WriteLine("S {0}.Send({1}, {2})", Id, source, message);

			stack.Add(this);

			var items = from i in Items.OfType<IMessageSink>() where stack.Contains(i) == false select i;
			if (items.Any(i => i.Send(this, message, stack))) return true;

			if (Parent == null || Parent == source) return false;

			var p = Parent.GetValue(MessageSyncProperty);
			return p == null ? false : p.Send(this, message, stack);
		}

		#endregion
	}

	public abstract class RulesStateBase<TPlant> : RulesStateBase<TPlant, object>
	{
	}

	public class RulesState : RulesStateBase<object, object>
	{
	}
}